﻿using System;
using System.Windows;
using System.Windows.Media;
using System.Windows.Media.Imaging;

namespace ImageProcessor.Shared.ImageExtensions
{
    public static class WriteableBitmapHelper
    {
#if SILVERLIGHT
        public static WriteableBitmap CreateBitmap(int width, int height)
        {
            return new WriteableBitmap(width, height);
        }

        public static void SetEachPixelColour(
            this WriteableBitmap bitmap,
            Func<Point, PixelColor> colourFunc)
        {
            Point point = new Point(0, 0);
            int[] pixels = bitmap.Pixels;
            int pixelPos = 0;
            for (int y = 0; y < bitmap.PixelHeight; y++)
            {
                point.Y = y;
                for (int x = 0; x < bitmap.PixelWidth; x++)
                {
                    point.X = x;
                    PixelColor color = colourFunc(point);
                    pixels[pixelPos++] = color.AsInt;
                }
            }
        }
#else
        public static WriteableBitmap CreateBitmap(int width, int height)
        {
            return new WriteableBitmap(
                width,
                height,
                96,
                96,
                System.Windows.Media.PixelFormats.Bgr32,
                null);
        }

        public static void SetEachPixelColour(
            this WriteableBitmap bitmap,
            Func<Int32Point, PixelColor> colourFunc)
        {
            // See http://msdn.microsoft.com/en-us/library/system.windows.media.imaging.writeablebitmap.aspx
            unsafe
            {
                // Get a pointer to the back buffer.
                bitmap.Lock();
                var point = new Int32Point(0, 0);
                int backBuffer = (int)bitmap.BackBuffer;

                for (int y = 0; y < bitmap.PixelHeight; y++)
                {
                    point.Y = y;
                    for (int x = 0; x < bitmap.PixelWidth; x++)
                    {
                        point.X = x;

                        // Find the address of the pixel to draw.
                        PixelColor color = colourFunc(point);

                        // Assign the color data to the pixel.
                        *((int*)backBuffer) = color.AsInt;

                        // Step to the next pixel - 4 bytes per pixel
                        backBuffer += 4;
                    }
                }

                // The entire bitmap has been regenerated
                bitmap.AddDirtyRect(new Int32Rect(0, 0, bitmap.PixelWidth, bitmap.PixelHeight));
                bitmap.Unlock();
            }
        }
#endif
        public static Color GetPixel(this WriteableBitmap wbm, int x, int y)
        {
            if (y > wbm.PixelHeight - 1 ||
              x > wbm.PixelWidth - 1)
                return Color.FromArgb(0, 0, 0, 0);
            if (y < 0 || x < 0)
                return Color.FromArgb(0, 0, 0, 0);
//            if (!wbm.Format.Equals(
//                    PixelFormats.Bgra32))
//                return Color.FromArgb(0, 0, 0, 0); ;
            IntPtr buff = wbm.BackBuffer;
            int Stride = wbm.BackBufferStride;
            Color c;
            unsafe
            {
                byte* pbuff = (byte*)buff.ToPointer();
                int loc = y * Stride + x * 4;
                c = Color.FromArgb(pbuff[loc + 3],
                  pbuff[loc + 2], pbuff[loc + 1],
                    pbuff[loc]);
            }
            return c;
        }

        public static void SetPixel(this WriteableBitmap wbm, int x, int y, Color c)
        {
            if (y > wbm.PixelHeight - 1 ||
                x > wbm.PixelWidth - 1) return;
            if (y < 0 || x < 0) return;
            if (!wbm.Format.Equals(PixelFormats.Bgra32)) return;
            wbm.Lock();
            IntPtr buff = wbm.BackBuffer;
            int Stride = wbm.BackBufferStride;

            unsafe
            {
                byte* pbuff = (byte*)buff.ToPointer();
                int loc = y * Stride + x * 4;
                pbuff[loc] = c.B;
                pbuff[loc + 1] = c.G;
                pbuff[loc + 2] = c.R;
                pbuff[loc + 3] = c.A;
            }
            wbm.AddDirtyRect(new Int32Rect(x, y, 1, 1));
            wbm.Unlock();
        }
    }

    public struct Int32Point
    {
        public Int32Point(int x, int y) : this()
        {
            X = x;
            Y = y;
        }

        public int X { get; set; }
        public int Y { get; set; }
    }
}